/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package elaborate.h2x.transform.position;

import elaborate.h2x.parser.TransformingModelParserBuilder;
import elaborate.h2x.parser.TransformingModelParserBuilderFactory;
import elaborate.h2x.transform.TransformingModel;
import elaborate.h2x.transform.TransformingNode;
import elaborate.h2x.transform.TransformingNodeId;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;

/**
 *
 * @author lendle
 */
public class DefaultPositionMapImpl implements PositionMap{

    protected List<TransformingNodeId> roots=new ArrayList<TransformingNodeId>();
    //map from parent node to the PositionSlot of its first child
    protected Map<TransformingNodeId, PositionSlot> parentChildMap=new HashMap<TransformingNodeId, PositionSlot>();
    //map from a node to its PositionSlot
    protected Map<TransformingNodeId, PositionSlot> positionSlotMap=new HashMap<TransformingNodeId, PositionSlot>();
    /**
     * Get the value of roots
     *
     * @return the value of roots
     */
    public List<TransformingNodeId> getRoots() {
        return roots;
    }
    /**
     * position or reposition a node
     * @param parentNode
     * @param previousSibling
     * @param node
     * @throws Exception
     */
    public PositionSlot positionNode(TransformingNodeId parentNode, TransformingNodeId previousSibling, TransformingNodeId node) throws Exception{
        if(parentNode==null){
            throw new Exception("wrong parent node: "+parentNode+"!!");
        }
        PositionSlot slot=positionSlotMap.get(node);
        if(slot!=null){
            //System.out.println(slot.getParent());
            if(((previousSibling==null && slot.getPrev()==null) || (previousSibling!=null && previousSibling.equals(slot.getPrev()))) && slot.getParent().equals(parentNode)){
                //no need to modify
                return slot;
            }
            this.resetSlotFromPositionMap(slot);
        }
        else{
            slot=new PositionSlot();
            slot.setNodeId(node);
            this.positionSlotMap.put(node, slot);
        }

        if(previousSibling!=null){
            PositionSlot ref=this.positionSlotMap.get(previousSibling);
            slot.setParent(ref.getParent());
            slot.setPrev(ref.getNodeId());
            slot.setNext(ref.getNext());
            if(ref.getNext()!=null){
                slot.setNext(ref.getNext());
                positionSlotMap.get(ref.getNext()).setPrev(node);
            }
            ref.setNext(slot.getNodeId());
            //System.out.println("\t"+ref+":"+ref.getParent()+":"+positionSlotMap.get(ref.getParent())+":"+this.getOrderedChildList(ref.getParent()).size());
        }
        else{
            //no previous sibling, position this node as the first child
            //node of the given parent node reference
            PositionSlot firstChildSlot=this.parentChildMap.get(parentNode);
            if(firstChildSlot!=null){
                firstChildSlot.setPrev(node);
                slot.setParent(parentNode);
                slot.setNext(firstChildSlot.getNodeId());
                this.parentChildMap.put(parentNode, slot);
            }
            else{
                //parent slot is not found, this is the first child node of the parent node
                slot.setParent(parentNode);
                this.parentChildMap.put(parentNode, slot);
            }
        }
        //System.out.println("\t"+parentNode+", "+this.getOrderedChildList(parentNode).size());
        return slot;
    }

    protected void resetSlotFromPositionMap(PositionSlot positionSlot){
        //process previous sibling relationship at first
        PositionSlot next=this.positionSlotMap.get(positionSlot.getNext());
        PositionSlot prev=this.positionSlotMap.get(positionSlot.getPrev());
        //System.out.println("reset "+positionSlot.getNodeId()+":"+prev+":"+next);
        if(prev!=null){
            prev.setNext((next!=null)?next.getNodeId():null);
            if(next!=null){
                next.setPrev(prev.getNodeId());
            }
        }
        else{
            this.parentChildMap.remove(positionSlot.getParent());
            if(next!=null){
                this.parentChildMap.put(positionSlot.getParent(), next);
                next.setPrev(null);
            }
        }
        ///////////////////////////////////
        //process parent child relationship
        /*PositionSlot firstChild=this.parentChildMap.get(positionSlot.getNodeId());
        if(firstChild!=null){
            
        }*/
        ///////////////////////////////////
        //this.positionSlotMap.remove(positionSlot.getNodeId());
    }

    public List<TransformingNodeId> getOrderedChildList(TransformingNodeId nodeId) {
        List<TransformingNodeId> ret=new ArrayList<TransformingNodeId>();
        PositionSlot slot=this.parentChildMap.get(nodeId);
        while(slot!=null){
            ret.add(slot.getNodeId());
            slot=this.positionSlotMap.get(slot.getNext());
        }
        return ret;
    }

    public TransformingNodeId getParentNode(TransformingNodeId nodeId) {
        PositionSlot slot=this.positionSlotMap.get(nodeId);
        if(slot!=null){
            return slot.getParent();
        }
        else{
            return null;
        }
    }

    public TransformingNodeId getPreviousSibling(TransformingNodeId nodeId) {
        PositionSlot slot=this.positionSlotMap.get(nodeId);
        if(slot!=null){
            return slot.getPrev();
        }
        else{
            return null;
        }
    }

    public TransformingNodeId getNextSibling(TransformingNodeId nodeId) {
        PositionSlot slot=this.positionSlotMap.get(nodeId);
        if(slot!=null){
            return slot.getNext();
        }
        else{
            return null;
        }
    }

    public static void main(String [] args) throws Exception{
        TransformingModelParserBuilderFactory tmf=TransformingModelParserBuilderFactory.newInstance();
        TransformingModelParserBuilder tmb=tmf.newTransformingModelParserBuilder();

        DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db=dbf.newDocumentBuilder();
        Document doc=db.parse(new File("src\\example1.xhtml"));

        TransformingModel model=tmb.newTransformingModelParser().parse(doc);

        LayeredTransformingNodesPositionerImpl positioner=new LayeredTransformingNodesPositionerImpl();
        positioner.addTransformingNodesPositioner(new NaturalTransformingNodesPositionerImpl());

        DefaultPositionMapImpl positionMap=new DefaultPositionMapImpl();
//        DefaultMutableTreeNode rootNode=new DefaultMutableTreeNode("root");
        List<TransformingNode> roots=model.getRoots();
        for(TransformingNode root : roots){
            positionMap.getRoots().add(root.getId());
        }

        positioner.determinePositions(model, positionMap);

        for(TransformingNodeId root : positionMap.getRoots()){
            printSubTree(model, positionMap, root, 0);
        }

        /*positionMap.resetSlotFromPositionMap(positionMap.positionSlotMap.get(TransformingNodeId.fromString("title1")));
        for(TransformingNodeId root : positionMap.getRoots()){
            printSubTree(model, positionMap, root, 0);
        }*/
        positionMap.positionNode(TransformingNodeId.fromString("book1"), TransformingNodeId.fromString("author1"), TransformingNodeId.fromString("title1"));
        //positionMap.resetSlotFromPositionMap(positionMap.positionSlotMap.get(TransformingNodeId.fromString("title1")));
        for(TransformingNodeId root : positionMap.getRoots()){
            printSubTree(model, positionMap, root, 0);
        }
        positionMap.positionNode(TransformingNodeId.fromString("author1"), null, TransformingNodeId.fromString("title1"));
        //positionMap.resetSlotFromPositionMap(positionMap.positionSlotMap.get(TransformingNodeId.fromString("title1")));
        for(TransformingNodeId root : positionMap.getRoots()){
            printSubTree(model, positionMap, root, 0);
        }
//        DefaultTreeModel treeModel=new DefaultTreeModel(rootNode);
    }

    private static void printSubTree(TransformingModel model, DefaultPositionMapImpl positionMap, TransformingNodeId root, int level){
        for(int i=0; i<level*2; i++){
            System.out.print(" ");
        }
        System.out.println(model.getNode(root).getDef().getNode());
        PositionSlot childSlot=positionMap.parentChildMap.get(root);
        while(childSlot!=null){
            printSubTree(model, positionMap, childSlot.getNodeId(), level+1);
            childSlot=positionMap.positionSlotMap.get(childSlot.getNext());
        }
    }

    public PositionSlot getPositionSlot(TransformingNodeId nodeId) {
        return this.positionSlotMap.get(nodeId);
    }
}
